Model Deployment : Exploring Modular Application Programming Interface Frameworks For Serving Model Predictions¶
- 1. Table of Contents
- 1.1 Project Background
- 1.2 Application Programming Interface (API) Development Using the FastAPI Framework
- 1.3 Application Programming Interface (API) Development Using the Flask Framework
- 1.4 Consolidated Findings
- 2. Summary
- 3. References
1. Table of Contents ¶
This project explores the modular deployment of machine learning models using Representational State Transfer (RESTful) Application Programming Interfaces (APIs), specifically comparing FastAPI and Flask frameworks in Python. Pre-trained models were loaded and integrated into the APIs, including a Stacked Ensemble binary classification model for predicting lung cancer probabilities, a Cox Proportional Hazards survival prediction model for estimating heart failure survival profiles, and a Convolutional Neural Network-based image classification model for determining class categories for brain magnetic resonance images. The study objectives included understanding the similarities and differences between FastAPI and Flask in implementating and documenting RESTful API endpoints to process data preprocessing and model prediction logic, handling a variety of input types (such as structured data for class and survival probability predictions and file uploads for image classification), enabling diverse output formats (including string, float, list and base64-encoded visualization objects, depending on the model), and ensuring robust error handling and validation. All results were consolidated in a Summary presented at the end of the document.
RESTful APIs are a standardized architectural style for designing networked applications, enabling communication between clients and servers over HTTP. They use HTTP methods like GET, POST, PUT, and DELETE to perform CRUD (Create, Read, Update, Delete) operations on resources, which are typically represented in JSON or XML format. RESTful APIs are stateless, meaning each request from a client to a server must contain all the information needed to process the request, ensuring scalability and reliability. For machine learning model deployment, RESTful APIs serve as a bridge between trained models and end-users or applications, allowing models to be accessed remotely via HTTP requests. This enables real-time predictions, batch processing, and integration with web or mobile applications. RESTful APIs are particularly significant for machine learning because they provide a standardized, platform-agnostic way to serve predictions, making models accessible to a wide range of clients. They also facilitate modularity, as models can be updated or replaced without affecting the client-side application. Additionally, RESTful APIs support scalability, as they can be deployed on cloud platforms and scaled horizontally to handle increased traffic. Error handling and validation mechanisms in RESTful APIs ensure robustness, which is critical for machine learning applications where malformed inputs can lead to incorrect predictions. By encapsulating machine learning logic behind APIs, developers can abstract away the complexity of model inference, making it easier for non-technical users to interact with the models. RESTful APIs also enable versioning, allowing multiple versions of a model to coexist and be accessed independently. This is particularly useful for A/B testing or gradual rollouts of updated models. Furthermore, RESTful APIs can be secured using authentication and authorization mechanisms, ensuring that only authorized users or applications can access the model. Overall, RESTful APIs are a cornerstone of modern machine learning deployment, providing a flexible, scalable, and secure way to serve predictions in production environments.
FastAPI is a modern, high-performance web framework for building APIs with Python, specifically designed for speed and ease of use. It is built on top of Starlette for web handling and Pydantic for data validation, making it one of the fastest Python frameworks available. FastAPI leverages Python type hints to automatically generate OpenAPI (Swagger) documentation, which simplifies API testing and debugging. Its asynchronous capabilities, powered by Python’s async and await keywords, make it ideal for handling high-concurrency workloads, such as serving machine learning models to multiple clients simultaneously. FastAPI’s built-in dependency injection system allows for modular and reusable code, which is particularly useful for complex machine learning pipelines. The framework also supports WebSocket communication, enabling real-time interactions, though this is less commonly used in machine learning deployments. One of FastAPI’s key strengths is its automatic data validation using Pydantic, which ensures that inputs to machine learning models are correctly formatted and reduces the risk of errors during inference. However, FastAPI’s reliance on asynchronous programming can be a double-edged sword; while it improves performance, it may require developers to have a deeper understanding of asynchronous programming concepts. Additionally, FastAPI’s ecosystem, while growing, is still smaller than that of more established frameworks like Flask, which can limit the availability of third-party plugins and extensions. Despite these limitations, FastAPI is widely regarded as an excellent choice for machine learning model deployment due to its speed, scalability, and developer-friendly features. Its ability to automatically generate API documentation and validate inputs makes it particularly well-suited for production environments where reliability and maintainability are critical.
Flask Flask is a lightweight and flexible web framework for Python, designed to be simple and easy to use while providing the essentials for building web applications and APIs. It follows the WSGI (Web Server Gateway Interface) standard and is often described as a "micro-framework" because it provides only the core components needed for web development, such as routing and request handling, while leaving other functionalities to extensions. Flask’s simplicity and minimalistic design make it highly customizable, allowing developers to tailor it to specific use cases, including machine learning model deployment. Flask’s synchronous nature makes it easier to understand and use for developers who are not familiar with asynchronous programming, though this can limit its performance in high-concurrency scenarios. For machine learning deployments, Flask’s simplicity is both a strength and a weakness; while it is easy to set up and deploy, it lacks built-in features like data validation and automatic documentation, which must be implemented manually or through extensions like Flasgger. Flask’s extensive ecosystem of extensions, such as Flask-RESTful for building RESTful APIs and Flask-SQLAlchemy for database integration, provides additional functionality but can also introduce complexity. One of Flask’s key strengths is its widespread adoption and community support, which makes it easier to find tutorials, documentation, and third-party tools. However, Flask’s synchronous architecture can become a bottleneck when serving machine learning models to multiple clients simultaneously, as it may struggle to handle high traffic efficiently. Despite these limitations, Flask remains a popular choice for machine learning model deployment, particularly for smaller-scale applications or prototypes where simplicity and ease of use are prioritized over performance. Its flexibility and extensive ecosystem make it a versatile tool for developers, though it may require more effort to achieve the same level of robustness and scalability as FastAPI.
1.1. Project Background ¶
1.1.1 Categorical Classification ¶
1.1.1.1 Data Background ¶
1.1.1.2 Model Background ¶
1.1.1.3 Deployment Background ¶
1.1.2 Survival Prediction ¶
1.1.2.1 Data Background ¶
1.1.2.2 Model Background ¶
1.1.2.3 Deployment Background ¶
1.1.3 Image Classification ¶
1.1.3.1 Data Background ¶
1.1.3.2 Model Background ¶
1.1.3.3 Deployment Background ¶
1.2. Application Programming Interface (API) Development Using the FastAPI Framework ¶
1.2.1 Categorical Classification ¶
1.2.1.1 API Building ¶
1.2.1.2 API Testing ¶
##################################
# Loading Python Libraries
##################################
import requests
##################################
# Defining the base URL of the API
# for the categorical classification model
##################################
CC_FASTAPI_BASE_URL = "http://127.0.0.1:8000"
##################################
# Defining the input values for an individual test case
##################################
individual_test_case = {
"features_individual": [1, 0, 0, 0, 0, 1, 0, 0, 1, 1]
}
##################################
# Defining the input values for a batch of cases
##################################
batch_test_case = {
"features_list": [
[1, 0, 0, 0, 0, 1, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 1, 0, 1, 1, 1]
]
}
##################################
# Generating a GET endpoint request for
# for validating API service connection
##################################
response = requests.get(f"{CC_FASTAPI_BASE_URL}/")
if response.status_code == 200:
print("Root Endpoint Response:", response.json())
else:
print("Error:", response.status_code, response.text)
Root Endpoint Response: {'message': 'Welcome to the Categorical Classification API!'}
##################################
# Generating a POST endpoint request for
# computing the risk index,
# estimating the lung cancer probability,
# and predicting the risk category
# of an individual test case
##################################
response = requests.post(f"{CC_FASTAPI_BASE_URL}/predict-individual-logit-probability-class", json=individual_test_case)
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
'Response:'
{'logit': -1.2117837409390746,
'probability': 0.22938559072691203,
'risk_class': 'Low-Risk'}
##################################
# Sending a POST endpoint request for
# computing the risk index,
# estimating the lung cancer probability,
# and predicting the risk category
# of a list of train cases
##################################
response = requests.post(f"{CC_FASTAPI_BASE_URL}/predict-list-logit-probability-class", json=batch_test_case)
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
'Response:'
{'logit': [-1.2117837409390746, 3.4784950973590973],
'probability': [0.22938559072691203, 0.9700696569701589],
'logit_sorted': [-1.2117837409390746, 3.4784950973590973],
'probability_sorted': [0.22938559072691203, 0.9700696569701589]}
##################################
# Sending a POST endpoint request
# using malformed data to evaluate
# the API's error handling function
##################################
malformed_test_case = {"features": [1, 0, 1]}
response = requests.post(f"{CC_FASTAPI_BASE_URL}/predict-individual-logit-probability-class", json=malformed_test_case)
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
Error: 422 {"detail":[{"type":"missing","loc":["body","features_individual"],"msg":"Field required","input":{"features":[1,0,1]}}]}
1.2.2 Survival Prediction ¶
1.2.2.1 API Building ¶
1.2.2.2 API Testing ¶
##################################
# Loading Python Libraries
##################################
import requests
import json
import pandas as pd
import base64
from IPython.display import display
from PIL import Image
##################################
# Defining the base URL of the API
# for the survival prediction model
##################################
SP_FASTAPI_BASE_URL = "http://127.0.0.1:8001"
##################################
# Defining the input values for an individual test case
##################################
single_test_case = {
"features_individual": [43, 0, 75, 1, 0.75, 100]
}
##################################
# Defining the input values for a batch of cases
##################################
train_list = {
"features_list": [
[43, 0, 75, 1, 0.75, 100],
[70, 1, 20, 1, 0.75, 100]
]
}
##################################
# Defining the input values for a batch of cases for binning request
##################################
bin_request = {
"X_original_list": [
{"AGE": -0.10, "EJECTION_FRACTION": -0.10, "SERUM_CREATININE ": -0.10, "SERUM_SODIUM": -0.10},
{"AGE": 0.20, "EJECTION_FRACTION": 0.20, "SERUM_CREATININE ": 0.20, "SERUM_SODIUM": 0.20},
{"AGE": 0.90, "EJECTION_FRACTION": 0.90, "SERUM_CREATININE ": 0.90, "SERUM_SODIUM": 0.90}
],
"numeric_feature": "AGE"
}
##################################
# Defining the input values for a batch of cases for Kaplan-Meier plotting
##################################
km_request = {
"df": [
{"TIME": 0, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 25, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 50, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 100, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 125, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 150, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 175, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 200, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 225, "DEATH_EVENT": 1, "AGE": "Low"},
{"TIME": 250, "DEATH_EVENT": 1, "AGE": "Low"},
{"TIME": 0, "DEATH_EVENT": 0, "AGE": "High"},
{"TIME": 25, "DEATH_EVENT": 0, "AGE": "High"},
{"TIME": 50, "DEATH_EVENT": 0, "AGE": "High"},
{"TIME": 100, "DEATH_EVENT": 1, "AGE": "High"},
{"TIME": 125, "DEATH_EVENT": 0, "AGE": "High"},
{"TIME": 150, "DEATH_EVENT": 0, "AGE": "High"},
{"TIME": 175, "DEATH_EVENT": 1, "AGE": "High"},
{"TIME": 200, "DEATH_EVENT": 1, "AGE": "High"},
{"TIME": 225, "DEATH_EVENT": 1, "AGE": "High"},
{"TIME": 250, "DEATH_EVENT": 1, "AGE": "High"},
],
"cat_var": "AGE",
"new_case_value": "Low"
}
##################################
# Generating a GET endpoint request for
# for validating API service connection
##################################
response = requests.get(f"{SP_FASTAPI_BASE_URL}/")
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
'Response:'
{'message': 'Welcome to the Survival Prediction API!'}
##################################
# Sending a POST endpoint request for
# generating the heart failure survival profile,
# estimating the heart failure survival probabilities,
# and predicting the risk category
# of an individual test case
##################################
response = requests.post(f"{SP_FASTAPI_BASE_URL}/compute-individual-coxph-survival-probability-class/", json=single_test_case)
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
'Response:'
{'survival_function': [0.9973812917524568,
0.9920416812438736,
0.9893236791425079,
0.972381113071464,
0.9693179903073035,
0.9631930672135339,
0.9631930672135339,
0.9600469571766689,
0.9600469571766689,
0.9568596864927983,
0.9536305709158891,
0.9471625843882805,
0.93729581350105,
0.9338986486591409,
0.93048646553474,
0.9270645831787163,
0.9202445006124622,
0.9167715111530355,
0.9132845175345189,
0.9097550958520674,
0.9097550958520674,
0.9097550958520674,
0.9060810720432387,
0.9024157452999795,
0.9024157452999795,
0.9024157452999795,
0.9024157452999795,
0.9024157452999795,
0.8985598696587259,
0.8985598696587259,
0.8985598696587259,
0.8945287485160898,
0.8945287485160898,
0.8945287485160898,
0.8945287485160898,
0.8901959645503091,
0.8812352215018253,
0.8812352215018253,
0.8812352215018253,
0.8812352215018253,
0.8764677174183527,
0.8764677174183527,
0.8764677174183527,
0.8764677174183527,
0.8709113650481243,
0.8709113650481243,
0.8652494086650531,
0.8593884303802698,
0.8593884303802698,
0.8593884303802698,
0.8593884303802698,
0.8593884303802698,
0.8528574859874233,
0.8528574859874233,
0.8528574859874233,
0.8528574859874233,
0.8528574859874233,
0.8459534502216807,
0.8389821875092403,
0.8319419786276306,
0.8246669811915435,
0.8099879066057215,
0.8099879066057215,
0.7943979200335176,
0.7943979200335176,
0.7943979200335176,
0.7943979200335176,
0.7943979200335176,
0.7848178617845467,
0.7848178617845467,
0.7848178617845467,
0.7848178617845467,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7555469848652164,
0.7555469848652164,
0.7555469848652164,
0.7555469848652164,
0.7555469848652164,
0.7337716342207724,
0.7337716342207724,
0.7337716342207724,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696],
'survival_time': [50, 100, 150, 200, 250],
'survival_probabilities': [90.97550958520674,
87.64677174183527,
84.59534502216806,
78.48178617845467,
70.70184115456696],
'risk_category': 'Low-Risk'}
##################################
# Sending a POST endpoint request for
# generating the heart failure survival profile and
# estimating the heart failure survival probabilities
# of a list of train cases
##################################
response = requests.post(f"{SP_FASTAPI_BASE_URL}/compute-list-coxph-survival-profile/", json=train_list)
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
'Response:'
{'survival_profiles': [[0.9973812917524568,
0.9920416812438736,
0.9893236791425079,
0.972381113071464,
0.9693179903073035,
0.9631930672135339,
0.9631930672135339,
0.9600469571766689,
0.9600469571766689,
0.9568596864927983,
0.9536305709158891,
0.9471625843882805,
0.93729581350105,
0.9338986486591409,
0.93048646553474,
0.9270645831787163,
0.9202445006124622,
0.9167715111530355,
0.9132845175345189,
0.9097550958520674,
0.9097550958520674,
0.9097550958520674,
0.9060810720432387,
0.9024157452999795,
0.9024157452999795,
0.9024157452999795,
0.9024157452999795,
0.9024157452999795,
0.8985598696587259,
0.8985598696587259,
0.8985598696587259,
0.8945287485160898,
0.8945287485160898,
0.8945287485160898,
0.8945287485160898,
0.8901959645503091,
0.8812352215018253,
0.8812352215018253,
0.8812352215018253,
0.8812352215018253,
0.8764677174183526,
0.8764677174183526,
0.8764677174183526,
0.8764677174183526,
0.8709113650481243,
0.8709113650481243,
0.8652494086650531,
0.8593884303802697,
0.8593884303802697,
0.8593884303802697,
0.8593884303802697,
0.8593884303802697,
0.8528574859874233,
0.8528574859874233,
0.8528574859874233,
0.8528574859874233,
0.8528574859874233,
0.8459534502216807,
0.8389821875092403,
0.8319419786276306,
0.8246669811915435,
0.8099879066057215,
0.8099879066057215,
0.7943979200335176,
0.7943979200335176,
0.7943979200335176,
0.7943979200335176,
0.7943979200335176,
0.7848178617845467,
0.7848178617845467,
0.7848178617845467,
0.7848178617845467,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7555469848652164,
0.7555469848652164,
0.7555469848652164,
0.7555469848652164,
0.7555469848652164,
0.7337716342207724,
0.7337716342207724,
0.7337716342207724,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695],
[0.9761144218801228,
0.928980888267716,
0.905777064852962,
0.7724242339590301,
0.7502787164583535,
0.7076872741961866,
0.7076872741961866,
0.6866593185026403,
0.6866593185026403,
0.6659260634219393,
0.6454915885099762,
0.6062342264686207,
0.5504405490863784,
0.5323184765768243,
0.5146536440658629,
0.49746533888245986,
0.464726413395405,
0.4488047347163327,
0.433309930422515,
0.4181140392975694,
0.4181140392975694,
0.4181140392975694,
0.4028020116306455,
0.38802639234746467,
0.38802639234746467,
0.38802639234746467,
0.38802639234746467,
0.38802639234746467,
0.373006008573048,
0.373006008573048,
0.373006008573048,
0.35785929480690143,
0.35785929480690143,
0.35785929480690143,
0.35785929480690143,
0.3421927616040032,
0.3117176431598899,
0.3117176431598899,
0.3117176431598899,
0.3117176431598899,
0.29651072362871467,
0.29651072362871467,
0.29651072362871467,
0.29651072362871467,
0.2796248763802668,
0.2796248763802668,
0.2633052706162029,
0.24731169874453887,
0.24731169874453887,
0.24731169874453887,
0.24731169874453887,
0.24731169874453887,
0.23051507888001282,
0.23051507888001282,
0.23051507888001282,
0.23051507888001282,
0.23051507888001282,
0.213871875082776,
0.19816204676152543,
0.18334919195124752,
0.16908728217620728,
0.1432835564355214,
0.1432835564355214,
0.119778195679268,
0.119778195679268,
0.119778195679268,
0.119778195679268,
0.119778195679268,
0.10710184631976834,
0.10710184631976834,
0.10710184631976834,
0.10710184631976834,
0.09446095671842468,
0.09446095671842468,
0.09446095671842468,
0.09446095671842468,
0.09446095671842468,
0.09446095671842468,
0.09446095671842468,
0.07544024472233593,
0.07544024472233593,
0.07544024472233593,
0.07544024472233593,
0.07544024472233593,
0.05761125190131533,
0.05761125190131533,
0.05761125190131533,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404]]}
##################################
# Sending a POST endpoint request for
# creating dichotomous bins for the numeric features
# of a list of train cases
##################################
response = requests.post(f"{SP_FASTAPI_BASE_URL}/bin-numeric-model-feature/", json=bin_request)
if response.status_code == 200:
display("Response:", pd.DataFrame(response.json()))
else:
print("Error:", response.status_code, response.text)
'Response:'
| AGE | EJECTION_FRACTION | SERUM_CREATININE | SERUM_SODIUM | |
|---|---|---|---|---|
| 0 | Low | -0.1 | -0.1 | -0.1 |
| 1 | High | 0.2 | 0.2 | 0.2 |
| 2 | High | 0.9 | 0.9 | 0.9 |
##################################
# Sending a POST endpoint request for
# plotting the estimated survival profiles
# using Kaplan-Meier Plots
##################################
response = requests.post(f"{SP_FASTAPI_BASE_URL}/plot-kaplan-meier/", json=km_request)
if response.status_code == 200:
plot_data = response.json()["plot"]
# Decoding and displaying the plot
img = base64.b64decode(plot_data)
with open("kaplan_meier_plot.png", "wb") as f:
f.write(img)
display(Image.open("kaplan_meier_plot.png"))
else:
print("Error:", response.status_code, response.text)
##################################
# Sending a POST endpoint request
# using malformed data to evaluate
# the API's error handling function
##################################
malformed_test_case = {"features": [43, 0, 75, 1, 0.75]}
response = requests.post(f"{SP_FASTAPI_BASE_URL}/compute-individual-coxph-survival-probability-class", json=malformed_test_case)
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
Error: 422 {"detail":[{"type":"missing","loc":["body","features_individual"],"msg":"Field required","input":{"features":[43,0,75,1,0.75]}}]}
1.2.3 Image Classification ¶
1.2.3.1 API Building ¶
1.2.3.2 API Testing ¶
##################################
# Loading Python Libraries
##################################
import requests
import json
import base64
from IPython.display import display
from PIL import Image
import matplotlib.pyplot as plt
import io
from tensorflow.keras.utils import load_img
import os
import mimetypes
##################################
# Defining the base URL of the API
# for the image classification model
##################################
IC_FASTAPI_BASE_URL = "http://127.0.0.1:8002"
##################################
# Defining the file path for an individual test image
##################################
IMAGES_PATH = r"image_classification_study\images"
image_path = (os.path.join("..",IMAGES_PATH, "test_image.jpg"))
##################################
# Automatically determining the filename and content type
##################################
image_path_filename = os.path.basename(image_path)
image_path_content_type, _ = mimetypes.guess_type(image_path)
##################################
# Visualizing the individual test image
##################################
try:
image = Image.open(image_path)
print(f"Image File Path: {image_path}")
print(f"Image Format: {image.format}")
print(f"Image Size: {image.size}")
print(f"Image Mode: {image.mode}")
except Exception as e:
print(f"Error loading image: {e}")
plt.imshow(image)
plt.axis('off')
plt.title("Test Image")
plt.show()
Image File Path: ..\image_classification_study\images\test_image.jpg Image Format: JPEG Image Size: (215, 234) Image Mode: RGB
##################################
# Generating a GET endpoint request for
# for validating API service connection
##################################
response = requests.get(f"{IC_FASTAPI_BASE_URL}/")
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
'Response:'
{'message': 'Welcome to the Image Classification API!'}
##################################
# Sending a POST endpoint request for
# ensuring that the file upload mechanism is working
# by returning the the file metadata
##################################
with open(image_path, "rb") as file:
files = {"file": (image_path_filename, file, image_path_content_type)}
response = requests.post(f"{IC_FASTAPI_BASE_URL}/test-file-upload/", files=files)
if response.status_code == 200:
result = response.json()
print("File Upload Test Result:")
print(f"Filename: {result['filename']}")
print(f"Content Type: {result['content_type']}")
print(f"Size: {result['size']} bytes")
else:
print(f"Error: {response.status_code} - {response.text}")
File Upload Test Result: Filename: test_image.jpg Content Type: image/jpeg Size: 12103 bytes
##################################
# Sending a POST endpoint request for
# predicting the image category and
# estimating class probabilities
# of an individual test image
##################################
with open(image_path, "rb") as file:
files = {"file": ("image.jpg", file, "image/jpeg")}
response = requests.post(f"{IC_FASTAPI_BASE_URL}/predict-image-category-class-probability/", files=files)
if response.status_code == 200:
result = response.json()
print("Prediction Result:")
print(f"Predicted Class: {result['predicted_class']}")
print("Probabilities:")
for cls, prob in result["probabilities"].items():
print(f"{cls}: {prob:.5f}%")
else:
print(f"Error: {response.status_code} - {response.text}")
Prediction Result: Predicted Class: Meningioma Probabilities: No Tumor: 12.94989% Glioma: 0.02788% Meningioma: 87.02222% Pituitary: 0.00002%
##################################
# Sending a POST endpoint request for
# formulating the gradient class activation map
# from the output of the first to third convolutional layers and
# and superimposing on the actual image
##################################
with open(image_path, "rb") as file:
files = {"file": ("image.jpg", file, "image/jpeg")}
response = requests.post(f"{IC_FASTAPI_BASE_URL}/visualize-image-gradcam/", files=files)
if response.status_code == 200:
plot_data = response.json()["plot"]
# Decoding and displaying the plot
img = base64.b64decode(plot_data)
with open("image_gradcam_plot.png", "wb") as f:
f.write(img)
display(Image.open("image_gradcam_plot.png"))
else:
print(f"Error: {response.status_code} - {response.text}")
##################################
# Defining the file path for an individual test image
##################################
IMAGES_PATH = r"image_classification_study\images"
malformed_image_path = (os.path.join("..",IMAGES_PATH, "test_image.png"))
##################################
# Automatically determining the filename and content type
##################################
malformed_image_path_filename = os.path.basename(image_path)
malformed_image_path_content_type, _ = mimetypes.guess_type(malformed_image_path)
##################################
# Sending a POST endpoint request
# using malformed data to evaluate
# the API's error handling function
##################################
with open(malformed_image_path, "rb") as file:
files = {"file": (malformed_image_path_filename, file, malformed_image_path_content_type)}
response = requests.post(f"{IC_FASTAPI_BASE_URL}/test-file-upload/", files=files)
if response.status_code == 200:
result = response.json()
print("File Upload Test Result:")
print(f"Filename: {result['filename']}")
print(f"Content Type: {result['content_type']}")
print(f"Size: {result['size']} bytes")
else:
print(f"Error: {response.status_code} - {response.text}")
Error: 400 - {"detail":"File must be a JPEG image"}
1.3. Application Programming Interface (API) Development Using the Flask Framework ¶
1.3.1 Categorical Classification ¶
1.3.1.1 API Building ¶
1.3.1.2 API Testing ¶
##################################
# Loading Python Libraries
##################################
import requests
##################################
# Defining the base URL of the API
# for the categorical classification model
##################################
CC_FLASKAPI_BASE_URL = "http://127.0.0.1:5000/"
##################################
# Defining the input values for an individual test case
##################################
individual_test_case = {
"features_individual": [1, 0, 0, 0, 0, 1, 0, 0, 1, 1]
}
##################################
# Defining the input values for a batch of cases
##################################
batch_test_case = {
"features_list": [
[1, 0, 0, 0, 0, 1, 0, 0, 1, 1],
[1, 0, 1, 0, 1, 1, 0, 1, 1, 1]
]
}
##################################
# Generating a GET endpoint request for
# validating API service connection
##################################
response = requests.get(f"{CC_FLASKAPI_BASE_URL}/")
if response.status_code == 200:
print("Root Endpoint Response:", response.json())
else:
print("Error:", response.status_code, response.text)
Root Endpoint Response: {'message': 'Welcome to the Categorical Classification API!'}
##################################
# Generating a POST endpoint request for
# computing the risk index,
# estimating the lung cancer probability,
# and predicting the risk category
# of an individual test case
##################################
response = requests.post(f"{CC_FLASKAPI_BASE_URL}/predict-individual-logit-probability-class", json=individual_test_case)
if response.status_code == 200:
print("Individual Test Case Response:", response.json())
else:
print("Error:", response.status_code, response.text)
Individual Test Case Response: {'logit': -1.2117837409390746, 'probability': 0.22938559072691203, 'risk_class': 'Low-Risk'}
##################################
# Sending a POST endpoint request for
# computing the risk index,
# estimating the lung cancer probability,
# and predicting the risk category
# of a list of train cases
##################################
response = requests.post(f"{CC_FLASKAPI_BASE_URL}/predict-list-logit-probability-class", json=batch_test_case)
if response.status_code == 200:
print("Batch Test Case Response:", response.json())
else:
print("Error:", response.status_code, response.text)
Batch Test Case Response: {'logit': [-1.2117837409390746, 3.4784950973590973], 'logit_sorted': [-1.2117837409390746, 3.4784950973590973], 'probability': [0.22938559072691203, 0.9700696569701589], 'probability_sorted': [0.22938559072691203, 0.9700696569701589]}
##################################
# Sending a POST endpoint request
# using malformed data to evaluate
# the API's error handling function
##################################
malformed_test_case = {"features": [1, 0, 1]}
response = requests.post(f"{CC_FLASKAPI_BASE_URL}/predict-individual-logit-probability-class", json=malformed_test_case)
if response.status_code == 200:
print("Malformed Test Case Response:", response.json())
else:
print("Error:", response.status_code, response.text)
Error: 400 {"error":"'features_individual'"}
1.3.2 Survival Prediction ¶
1.3.2.1 API Building ¶
1.3.2.2 API Testing ¶
##################################
# Loading Python Libraries
##################################
import requests
import json
import pandas as pd
import base64
from IPython.display import Image, display
##################################
# Defining the base URL of the API
# for the survival prediction model
##################################
SP_FLASKAPI_BASE_URL = "http://127.0.0.1:5001"
##################################
# Defining the input values for an individual test case
##################################
single_test_case = {
"features_individual": [43, 0, 75, 1, 0.75, 100]
}
##################################
# Defining the input values for a batch of cases
##################################
train_list = {
"features_list": [
[43, 0, 75, 1, 0.75, 100],
[70, 1, 20, 1, 0.75, 100]
]
}
##################################
# Defining the input values for a batch of cases for binning request
##################################
bin_request = {
"X_original_list": [
{"AGE": -0.10, "EJECTION_FRACTION": -0.10, "SERUM_CREATININE": -0.10, "SERUM_SODIUM": -0.10},
{"AGE": 0.20, "EJECTION_FRACTION": 0.20, "SERUM_CREATININE": 0.20, "SERUM_SODIUM": 0.20},
{"AGE": 0.90, "EJECTION_FRACTION": 0.90, "SERUM_CREATININE": 0.90, "SERUM_SODIUM": 0.90}
],
"numeric_feature": "AGE"
}
##################################
# Defining the input values for a batch of cases for Kaplan-Meier plotting
##################################
km_request = {
"df": [
{"TIME": 0, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 25, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 50, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 100, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 125, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 150, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 175, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 200, "DEATH_EVENT": 0, "AGE": "Low"},
{"TIME": 225, "DEATH_EVENT": 1, "AGE": "Low"},
{"TIME": 250, "DEATH_EVENT": 1, "AGE": "Low"},
{"TIME": 0, "DEATH_EVENT": 0, "AGE": "High"},
{"TIME": 25, "DEATH_EVENT": 0, "AGE": "High"},
{"TIME": 50, "DEATH_EVENT": 0, "AGE": "High"},
{"TIME": 100, "DEATH_EVENT": 1, "AGE": "High"},
{"TIME": 125, "DEATH_EVENT": 0, "AGE": "High"},
{"TIME": 150, "DEATH_EVENT": 0, "AGE": "High"},
{"TIME": 175, "DEATH_EVENT": 1, "AGE": "High"},
{"TIME": 200, "DEATH_EVENT": 1, "AGE": "High"},
{"TIME": 225, "DEATH_EVENT": 1, "AGE": "High"},
{"TIME": 250, "DEATH_EVENT": 1, "AGE": "High"},
],
"cat_var": "AGE",
"new_case_value": "Low"
}
##################################
# Generating a GET endpoint request for
# for validating API service connection
##################################
response = requests.get(f"{SP_FLASKAPI_BASE_URL}/")
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
'Response:'
{'message': 'Welcome to the Survival Prediction API!'}
##################################
# Sending a POST endpoint request for
# generating the heart failure survival profile,
# estimating the heart failure survival probabilities,
# and predicting the risk category
# of an individual test case
##################################
response = requests.post(f"{SP_FLASKAPI_BASE_URL}/compute-individual-coxph-survival-probability-class/", json=single_test_case)
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
'Response:'
{'risk_category': 'Low-Risk',
'survival_function': [0.9973812917524568,
0.9920416812438736,
0.9893236791425079,
0.972381113071464,
0.9693179903073035,
0.9631930672135339,
0.9631930672135339,
0.9600469571766689,
0.9600469571766689,
0.9568596864927983,
0.9536305709158891,
0.9471625843882805,
0.93729581350105,
0.9338986486591409,
0.93048646553474,
0.9270645831787163,
0.9202445006124622,
0.9167715111530355,
0.9132845175345189,
0.9097550958520674,
0.9097550958520674,
0.9097550958520674,
0.9060810720432387,
0.9024157452999795,
0.9024157452999795,
0.9024157452999795,
0.9024157452999795,
0.9024157452999795,
0.8985598696587259,
0.8985598696587259,
0.8985598696587259,
0.8945287485160898,
0.8945287485160898,
0.8945287485160898,
0.8945287485160898,
0.8901959645503091,
0.8812352215018253,
0.8812352215018253,
0.8812352215018253,
0.8812352215018253,
0.8764677174183527,
0.8764677174183527,
0.8764677174183527,
0.8764677174183527,
0.8709113650481243,
0.8709113650481243,
0.8652494086650531,
0.8593884303802698,
0.8593884303802698,
0.8593884303802698,
0.8593884303802698,
0.8593884303802698,
0.8528574859874233,
0.8528574859874233,
0.8528574859874233,
0.8528574859874233,
0.8528574859874233,
0.8459534502216807,
0.8389821875092403,
0.8319419786276306,
0.8246669811915435,
0.8099879066057215,
0.8099879066057215,
0.7943979200335176,
0.7943979200335176,
0.7943979200335176,
0.7943979200335176,
0.7943979200335176,
0.7848178617845467,
0.7848178617845467,
0.7848178617845467,
0.7848178617845467,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7555469848652164,
0.7555469848652164,
0.7555469848652164,
0.7555469848652164,
0.7555469848652164,
0.7337716342207724,
0.7337716342207724,
0.7337716342207724,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696,
0.7070184115456696],
'survival_probabilities': [90.97550958520674,
87.64677174183527,
84.59534502216806,
78.48178617845467,
70.70184115456696],
'survival_time': [50, 100, 150, 200, 250]}
##################################
# Sending a POST endpoint request for
# generating the heart failure survival profile and
# estimating the heart failure survival probabilities
# of a list of train cases
##################################
response = requests.post(f"{SP_FLASKAPI_BASE_URL}/compute-list-coxph-survival-profile/", json=train_list)
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
'Response:'
{'survival_profiles': [[0.9973812917524568,
0.9920416812438736,
0.9893236791425079,
0.972381113071464,
0.9693179903073035,
0.9631930672135339,
0.9631930672135339,
0.9600469571766689,
0.9600469571766689,
0.9568596864927983,
0.9536305709158891,
0.9471625843882805,
0.93729581350105,
0.9338986486591409,
0.93048646553474,
0.9270645831787163,
0.9202445006124622,
0.9167715111530355,
0.9132845175345189,
0.9097550958520674,
0.9097550958520674,
0.9097550958520674,
0.9060810720432387,
0.9024157452999795,
0.9024157452999795,
0.9024157452999795,
0.9024157452999795,
0.9024157452999795,
0.8985598696587259,
0.8985598696587259,
0.8985598696587259,
0.8945287485160898,
0.8945287485160898,
0.8945287485160898,
0.8945287485160898,
0.8901959645503091,
0.8812352215018253,
0.8812352215018253,
0.8812352215018253,
0.8812352215018253,
0.8764677174183526,
0.8764677174183526,
0.8764677174183526,
0.8764677174183526,
0.8709113650481243,
0.8709113650481243,
0.8652494086650531,
0.8593884303802697,
0.8593884303802697,
0.8593884303802697,
0.8593884303802697,
0.8593884303802697,
0.8528574859874233,
0.8528574859874233,
0.8528574859874233,
0.8528574859874233,
0.8528574859874233,
0.8459534502216807,
0.8389821875092403,
0.8319419786276306,
0.8246669811915435,
0.8099879066057215,
0.8099879066057215,
0.7943979200335176,
0.7943979200335176,
0.7943979200335176,
0.7943979200335176,
0.7943979200335176,
0.7848178617845467,
0.7848178617845467,
0.7848178617845467,
0.7848178617845467,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7741993572193384,
0.7555469848652164,
0.7555469848652164,
0.7555469848652164,
0.7555469848652164,
0.7555469848652164,
0.7337716342207724,
0.7337716342207724,
0.7337716342207724,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695,
0.7070184115456695],
[0.9761144218801228,
0.928980888267716,
0.905777064852962,
0.7724242339590301,
0.7502787164583535,
0.7076872741961866,
0.7076872741961866,
0.6866593185026403,
0.6866593185026403,
0.6659260634219393,
0.6454915885099762,
0.6062342264686207,
0.5504405490863784,
0.5323184765768243,
0.5146536440658629,
0.49746533888245986,
0.464726413395405,
0.4488047347163327,
0.433309930422515,
0.4181140392975694,
0.4181140392975694,
0.4181140392975694,
0.4028020116306455,
0.38802639234746467,
0.38802639234746467,
0.38802639234746467,
0.38802639234746467,
0.38802639234746467,
0.373006008573048,
0.373006008573048,
0.373006008573048,
0.35785929480690143,
0.35785929480690143,
0.35785929480690143,
0.35785929480690143,
0.3421927616040032,
0.3117176431598899,
0.3117176431598899,
0.3117176431598899,
0.3117176431598899,
0.29651072362871467,
0.29651072362871467,
0.29651072362871467,
0.29651072362871467,
0.2796248763802668,
0.2796248763802668,
0.2633052706162029,
0.24731169874453887,
0.24731169874453887,
0.24731169874453887,
0.24731169874453887,
0.24731169874453887,
0.23051507888001282,
0.23051507888001282,
0.23051507888001282,
0.23051507888001282,
0.23051507888001282,
0.213871875082776,
0.19816204676152543,
0.18334919195124752,
0.16908728217620728,
0.1432835564355214,
0.1432835564355214,
0.119778195679268,
0.119778195679268,
0.119778195679268,
0.119778195679268,
0.119778195679268,
0.10710184631976834,
0.10710184631976834,
0.10710184631976834,
0.10710184631976834,
0.09446095671842468,
0.09446095671842468,
0.09446095671842468,
0.09446095671842468,
0.09446095671842468,
0.09446095671842468,
0.09446095671842468,
0.07544024472233593,
0.07544024472233593,
0.07544024472233593,
0.07544024472233593,
0.07544024472233593,
0.05761125190131533,
0.05761125190131533,
0.05761125190131533,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404,
0.040906392932898404]]}
##################################
# Sending a POST endpoint request for
# creating dichotomous bins for the numeric features
# of a list of train cases
##################################
response = requests.post(f"{SP_FLASKAPI_BASE_URL}/bin-numeric-model-feature/", json=bin_request)
if response.status_code == 200:
display("Response:", pd.DataFrame(response.json()))
else:
print("Error:", response.status_code, response.text)
'Response:'
| AGE | EJECTION_FRACTION | SERUM_CREATININE | SERUM_SODIUM | |
|---|---|---|---|---|
| 0 | Low | -0.1 | -0.1 | -0.1 |
| 1 | High | 0.2 | 0.2 | 0.2 |
| 2 | High | 0.9 | 0.9 | 0.9 |
##################################
# Sending a POST endpoint request for
# plotting the estimated survival profiles
# using Kaplan-Meier Plots
##################################
response = requests.post(f"{SP_FLASKAPI_BASE_URL}/plot-kaplan-meier/", json=km_request)
if response.status_code == 200:
plot_data = response.json()["plot"]
# Decoding and displaying the plot
img = base64.b64decode(plot_data)
with open("kaplan_meier_plot.png", "wb") as f:
f.write(img)
display(Image("kaplan_meier_plot.png"))
else:
print("Error:", response.status_code, response.text)
##################################
# Sending a POST endpoint request
# using malformed data to evaluate
# the API's error handling function
##################################
malformed_test_case = {"features": [43, 0, 75, 1, 0.75]}
response = requests.post(f"{SP_FLASKAPI_BASE_URL}/compute-individual-coxph-survival-probability-class/", json=malformed_test_case)
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
Error: 400 {"error":"Missing 'features_individual' in request"}
1.3.3 Image Classification ¶
1.3.3.1 API Building ¶
1.3.3.2 API Testing ¶
##################################
# Loading Python Libraries
##################################
import requests
import json
import base64
from IPython.display import display
from PIL import Image
import matplotlib.pyplot as plt
import io
from tensorflow.keras.utils import load_img
import os
import mimetypes
##################################
# Defining the base URL of the API
# for the image classification model
##################################
IC_FLASKAPI_BASE_URL = "http://127.0.0.1:5002"
##################################
# Defining the file path for an individual test image
##################################
IMAGES_PATH = r"image_classification_study\images"
image_path = (os.path.join("..",IMAGES_PATH, "test_image.jpg"))
##################################
# Automatically determining the filename and content type
##################################
image_path_filename = os.path.basename(image_path)
image_path_content_type, _ = mimetypes.guess_type(image_path)
##################################
# Visualizing the individual test image
##################################
try:
image = Image.open(image_path)
print(f"Image File Path: {image_path}")
print(f"Image Format: {image.format}")
print(f"Image Size: {image.size}")
print(f"Image Mode: {image.mode}")
except Exception as e:
print(f"Error loading image: {e}")
plt.imshow(image)
plt.axis('off')
plt.title("Test Image")
plt.show()
Image File Path: ..\image_classification_study\images\test_image.jpg Image Format: JPEG Image Size: (215, 234) Image Mode: RGB
##################################
# Generating a GET endpoint request for
# for validating API service connection
##################################
response = requests.get(f"{IC_FLASKAPI_BASE_URL}/")
if response.status_code == 200:
display("Response:", response.json())
else:
print("Error:", response.status_code, response.text)
'Response:'
{'message': 'Welcome to the Image Classification API!'}
##################################
# Sending a POST endpoint request for
# ensuring that the file upload mechanism is working
# by returning the the file metadata
##################################
with open(image_path, "rb") as file:
files = {"file": (image_path_filename, file, image_path_content_type)}
response = requests.post(f"{IC_FLASKAPI_BASE_URL}/test-file-upload/", files=files)
if response.status_code == 200:
result = response.json()
print("File Upload Test Result:")
print(f"Filename: {result['filename']}")
print(f"Content Type: {result['content_type']}")
print(f"Size: {result['size']} bytes")
else:
print(f"Error: {response.status_code} - {response.text}")
File Upload Test Result: Filename: test_image.jpg Content Type: image/jpeg Size: 12103 bytes
##################################
# Sending a POST endpoint request for
# predicting the image category and
# estimating class probabilities
# of an individual test image
##################################
with open(image_path, "rb") as file:
files = {"file": ("image.jpg", file, "image/jpeg")}
response = requests.post(f"{IC_FLASKAPI_BASE_URL}/predict-image-category-class-probability/", files=files)
if response.status_code == 200:
result = response.json()
print("Prediction Result:")
print(f"Predicted Class: {result['predicted_class']}")
print("Probabilities:")
for cls, prob in result["probabilities"].items():
print(f"{cls}: {prob:.5f}%")
else:
print(f"Error: {response.status_code} - {response.text}")
Prediction Result: Predicted Class: Meningioma Probabilities: Glioma: 0.02788% Meningioma: 87.02222% No Tumor: 12.94989% Pituitary: 0.00002%
##################################
# Sending a POST endpoint request for
# formulating the gradient class activation map
# from the output of the first to third convolutional layers and
# and superimposing on the actual image
##################################
with open(image_path, "rb") as file:
files = {"file": ("image.jpg", file, "image/jpeg")}
response = requests.post(f"{IC_FLASKAPI_BASE_URL}/visualize-image-gradcam/", files=files)
if response.status_code == 200:
plot_data = response.json()["plot"]
# Decoding and displaying the plot
img = base64.b64decode(plot_data)
with open("image_gradcam_plot.png", "wb") as f:
f.write(img)
display(Image.open("image_gradcam_plot.png"))
else:
print(f"Error: {response.status_code} - {response.text}")
##################################
# Defining the file path for an individual test image
##################################
IMAGES_PATH = r"image_classification_study\images"
malformed_image_path = (os.path.join("..",IMAGES_PATH, "test_image.png"))
##################################
# Automatically determining the filename and content type
##################################
malformed_image_path_filename = os.path.basename(image_path)
malformed_image_path_content_type, _ = mimetypes.guess_type(malformed_image_path)
##################################
# Sending a POST endpoint request
# using malformed data to evaluate
# the API's error handling function
##################################
malformed_image_path = (os.path.join("..",IMAGES_PATH, "test_image.png"))
with open(malformed_image_path, "rb") as file:
files = {"file": (malformed_image_path_filename, file, malformed_image_path_content_type)}
response = requests.post(f"{IC_FLASKAPI_BASE_URL}/test-file-upload/", files=files)
if response.status_code == 200:
result = response.json()
print("File Upload Test Result:")
print(f"Filename: {result['filename']}")
print(f"Content Type: {result['content_type']}")
print(f"Size: {result['size']} bytes")
else:
print(f"Error: {response.status_code} - {response.text}")
Error: 400 - {"error":"File must be a JPEG image"}
1.4. Consolidated Findings ¶
2. Summary ¶
3. References ¶
- [Book] Building Machine Learning Powered Applications: Going From Idea to Product by Emmanuel Ameisen
- [Book] Designing Machine Learning Systems: An Iterative Process for Production-Ready Applications by Chip Huyen
- [Book] Machine Learning Bookcamp: Build a Portfolio of Real-Life Projects by Alexey Grigorev and Adam Newmark
- [Book] Building Machine Learning Pipelines: Automating Model Life Cycles with TensorFlow by Hannes Hapke and Catherine Nelson
- [Book] Hands-On APIs for AI and Data Science: Python Development with FastAPI by Ryan Day
- [Book] Managing Machine Learning Projects: From Design to Deployment by Simon Thompson
- [Book] Building Data Science Applications with FastAPI: Develop, Manage, and Deploy Efficient Machine Learning Applications with Python by François Voron
- [Book] Microservice APIs: Using Python, Flask, FastAPI, OpenAPI and More by Jose Haro Peralta
- [Book] Machine Learning Engineering with Python: Manage the Lifecycle of Machine Learning odels using MLOps with Practical Examples by Andrew McMahon
- [Book] Introducing MLOps: How to Scale Machine Learning in the Enterprise by Mark Treveil, Nicolas Omont, Clément Stenac, Kenji Lefevre, Du Phan, Joachim Zentici, Adrien Lavoillotte, Makoto Miyazaki and Lynn Heidmann
- [Book] Practical Python Backend Programming: Build Flask and FastAPI Applications, Asynchronous Programming, Containerization and Deploy Apps on Cloud by Tim Peters
- [Python Library API] NumPy by NumPy Team
- [Python Library API] pandas by Pandas Team
- [Python Library API] seaborn by Seaborn Team
- [Python Library API] matplotlib.pyplot by MatPlotLib Team
- [Python Library API] matplotlib.image by MatPlotLib Team
- [Python Library API] matplotlib.offsetbox by MatPlotLib Team
- [Python Library API] itertools by Python Team
- [Python Library API] operator by Python Team
- [Python Library API] sklearn.experimental by Scikit-Learn Team
- [Python Library API] sklearn.impute by Scikit-Learn Team
- [Python Library API] sklearn.linear_model by Scikit-Learn Team
- [Python Library API] sklearn.preprocessing by Scikit-Learn Team
- [Python Library API] scipy by SciPy Team
- [Python Library API] sklearn.tree by Scikit-Learn Team
- [Python Library API] sklearn.ensemble by Scikit-Learn Team
- [Python Library API] sklearn.svm by Scikit-Learn Team
- [Python Library API] sklearn.metrics by Scikit-Learn Team
- [Python Library API] sklearn.model_selection by Scikit-Learn Team
- [Python Library API] imblearn.over_sampling by Imbalanced-Learn Team
- [Python Library API] imblearn.under_sampling by Imbalanced-Learn Team
- [Python Library API] SciKit-Survival by SciKit-Survival Team
- [Python Library API] SciKit-Learn by SciKit-Learn Team
- [Python Library API] StatsModels by StatsModels Team
- [Python Library API] SciPy by SciPy Team
- [Python Library API] Lifelines by Lifelines Team
- [Python Library API] tensorflow by TensorFlow Team
- [Python Library API] keras by Keras Team
- [Python Library API] pil by Pillow Team
- [Python Library API] glob by glob Team
- [Python Library API] cv2 by OpenCV Team
- [Python Library API] os by os Team
- [Python Library API] random by random Team
- [Python Library API] keras.models by TensorFlow Team
- [Python Library API] keras.layers by TensorFlow Team
- [Python Library API] keras.wrappers by TensorFlow Team
- [Python Library API] keras.utils by TensorFlow Team
- [Python Library API] keras.optimizers by TensorFlow Team
- [Python Library API] keras.preprocessing.image by TensorFlow Team
- [Python Library API] keras.callbacks by TensorFlow Team
- [Python Library API] keras.metrics by TensorFlow Team
- [Python Library API] sklearn.metrics by Scikit-Learn Team
- [Python Library API] Streamlit by Streamlit Team
- [Python Library API] Streamlit Community Cloud by Streamlit Team
- [Article] ML - Deploy Machine Learning Models Using FastAPI by Dorian Machado (Medium)
- [Article] Deploying Machine Learning Models Using FastAPI by Kevin Njagi (Medium)
- [Article] Deploy Machine Learning API with FastAPI for Free by Aniket Maurya (Lightning.AI)
- [Article] How to Use FastAPI for Machine Learning by Cheuk Ting Ho (JetBrains.Com)
- [Article] Deploying and Hosting a Machine Learning Model with FastAPI and Heroku by Michael Herman (TestDriven.IO)
- [Article] A Practical Guide to Deploying Machine Learning Models by Bala Priya (MachineLearningMastery.Com)
- [Article] Using FastAPI to Deploy Machine Learning Models by Carl Handlin (Medium)
- [Article] How to Deploy a Machine Learning Model by Maarten Grootendorst (MaartenGrootendorst.Com)
- [Article] Accelerating Machine Learning Deployment: Unleashing the Power of FastAPI and Docker by Pratyush Khare (Medium)
- [Article] Containerize and Deploy ML Models with FastAPI & Docker by Hemachandran Dhinakaran (Medium)
- [Article] Quick Tutorial to Deploy Your ML models using FastAPI and Docker by Shreyansh Singh (GitHub)
- [Article] How to Deploying Machine Learning Models in Production by Umair Akram (Medium)
- [Article] Deploying a Machine Learning Model with FastAPI: A Comprehensive Guide by Muhammad Naveed Arshad (Medium)
- [Article] Deploy Machine Learning Model with REST API using FastAPI by Yusuf Berki Yazıcıoğlu (Medium)
- [Article] Deploying An ML Model With FastAPI — A Succinct Guide by Yash Prakash (Medium)
- [Article] How to Build a Machine Learning App with FastAPI: Dockerize and Deploy the FastAPI Application to Kubernetes by Bravin Wasike (Dev.TO)
- [Article] Building a Machine Learning Model API with Flask: A Step-by-Step Guide by Nilesh Shinde (Medium)
- [Article] Deploying Your Machine Learning Model as a REST API Using Flask by Emmanuel Oludare (Medium)
- [Article] Machine Learning Model Deployment on Heroku Using Flask by Charu Makhijani (Medium)
- [Article] Model Deployment using Flask by Ravindra Sharma (Medium)
- [Article] Deploy a Machine Learning Model using Flask: Step-By-Step by Claudio Sabato (CodeFather.Tech)
- [Article] How to Deploy a Machine Learning Model using Flask? by DataDance.AI Team (DataDance.AI)
- [Article] A Comprehensive Guide on Deploying Machine Learning Models with Flask by MachineLearningModels.Org Team (MachineLearningModels.Org)
- [Article] How to Deploy Machine Learning Models with Flask and Docker by Usama Malik (Medium)
- [Article] Deploying Machine Learning Models with Flask: A Step-by-Step Guide by Sukma Hanifa (Medium)
- [Article] Machine Learning Model Deployment on Heroku Using Flask by Charu Makhijani (Medium)
- [Article] Complete Guide on Model Deployment with Flask and Heroku by Tarek Ghanoum (Medium)
- [Article] Turning Machine Learning Models into APIs in Python by Sayak Paul (DataCamp)
- [Article] Machine Learning, Pipelines, Deployment and MLOps Tutorial by Moez Ali (DataCamp)
- [Video Tutorial] Machine Learning Models Deployment with Flask and Docker by Data Science Dojo (YouTube)
- [Video Tutorial] Deploy Machine Learning Model Flask by Stats Wire (YouTube)
- [Video Tutorial] Deploy Machine Learning Models with Flask | Using Render to host API and Get URL :Step-By-Step Guide by Prachet Shah (YouTube)
- [Video Tutorial] Deploy Machine Learning Model using Flask by Krish Naik (YouTube)
- [Video Tutorial] Deploy Your ML Model Using Flask Framework by MSFTImagine (YouTube)
- [Video Tutorial] Build a Machine Learning App From Scratch with Flask & Docker by Patrick Loeber (YouTube)
- [Video Tutorial] Deploying a Machine Learning Model to a Web with Flask and Python Anywhere by Prof. Phd. Manoel Gadi (YouTube)
- [Video Tutorial] End To End Machine Learning Project With Deployment Using Flask by Data Science Diaries (YouTube)
- [Video Tutorial] Publish ML Model as API or Web with Python Flask by Python ML Daily (YouTube)
- [Video Tutorial] Deploy a Machine Learning Model using Flask API to Heroku by Jackson Yuan (YouTube)
- [Video Tutorial] Deploying Machine Learning Model with FlaskAPI - CI/CD for ML Series by Anthony Soronnadi (YouTube)
- [Video Tutorial] Deploy ML model as Webservice | ML model deployment | Machine Learning | Data Magic by Data Magic (YouTube)
- [Video Tutorial] Deploying Machine Learning Model Using Flask by DataMites (YouTube)
- [Video Tutorial] ML Model Deployment With Flask On Heroku | How To Deploy Machine Learning Model With Flask | Edureka by Edureka (YouTube)
- [Video Tutorial] ML Model Deployment with Flask | Machine Learning & Data Science by Dan Bochman (YouTube)
- [Video Tutorial] How to Deploy ML Solutions with FastAPI, Docker, & AWS by Shaw Talebi (YouTube)
- [Video Tutorial] Deploy ML models with FastAPI, Docker, and Heroku | Tutorial by AssemblyAI (YouTube)
- [Video Tutorial] Machine Learning Model Deployment Using FastAPI by TheOyinbooke (YouTube)
- [Video Tutorial] Creating APIs For Machine Learning Models with FastAPI by NeuralNine (YouTube)
- [Video Tutorial] How To Deploy Machine Learning Models Using FastAPI-Deployment Of ML Models As API’s by Krish Naik (YouTube)
- [Video Tutorial] Machine Learning Model with FastAPI, Streamlit and Docker by CodeTricks (YouTube)
- [Video Tutorial] FastAPI Machine Learning Model Deployment | Python | FastAPI by Stats Wire (YouTube)
- [Video Tutorial] Deploying Machine Learning Models - Full Guide by NeuralNine (YouTube)
- [Video Tutorial] Model Deployment FAST API - Docker | Machine Learning Model Deployment pipeline | FastAPI VS Flask by 360DigiTMG (YouTube)
- [Video Tutorial] Build an AI app with FastAPI and Docker - Coding Tutorial with Tips by Patrick Loeber (YouTube)
- [Video Tutorial] Create a Deep Learning API with Python and FastAPI by DataQuest (YouTube)
- [Video Tutorial] Fast API Machine Learning Web App Tutorial + Deployment on Heroku by Greg Hogg (YouTube)
- [Course] Deeplearning.AI Machine Learning in Production by DeepLearning.AI Team (Coursera)
- [Course] IBM AI Workflow: Enterprise Model Deployment by IBM Team (Coursera)
- [Course] DataCamp Machine Learning Engineer Track by DataCamp Team (DataCamp)
- [Course] DataCamp Designing Machine Learning Workflows in Python by DataCamp Team (DataCamp)
- [Course] DataCamp Building APIs in Python by DataCamp Team (DataCamp)
from IPython.display import display, HTML
display(HTML("<style>.rendered_html { font-size: 15px; font-family: 'Trebuchet MS'; }</style>"))